package wsi.subgraph.entity;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.rygh.core.html.table.HtmlTable;
import org.rygh.core.html.table.TableData;
import org.rygh.core.html.table.TableTuple;
import org.rygh.core.model.common.MapList;
import org.rygh.semantic.model.ex.PredicatePath;

import wsi.subgraph.tree.EntityBranch;
import wsi.subgraph.tree.EntityLeaf;
import wsi.subgraph.tree.EntityTree;

public class EntityGroupController {

	private final MapList<Integer, EntityGroup> groupMapList = new MapList<>();
	private final Map<String, EntityGroup> groupMap = new HashMap<>();
	private final EntityTree entityTree;
	private int distance;

	public EntityGroupController(EntityTree entityTree) {
		this.entityTree = entityTree;
	}

	public int getDistance() {
		return distance;
	}

	public EntityTree getEntityTree() {
		return entityTree;
	}

	public MapList<Integer, EntityGroup> getGroupMapList() {
		return groupMapList;
	}

	public List<EntityGroup> getGroups(int distance) {
		return groupMapList.get(distance);
	}

	public EntityLeaf getInputEntity() {
		return getGroups(0).get(0).getNodes().get(0);
	}

	/**
	 * stratify the entity tree according to distance to center and relation
	 * path.
	 * 
	 */
	public void init() {
		int d = 0;

		EntityLeaf root = entityTree.getConcreteRoot();

		EntityGroup egroup = new EntityGroup(new PredicatePath());
		egroup.add(root);
		groupMapList.put(d, egroup);

		List<EntityLeaf> list = new ArrayList<>();
		List<EntityLeaf> next = new ArrayList<>();
		list.add(root);

		EntityBranch branch;
		PredicatePath path;
		String key;

		while (!list.isEmpty()) {
			d++;
			next.clear();

			for (EntityLeaf i : list) {
				if (i.isLeaf())
					continue;

				for (EntityLeaf j : i.getChildren()) {
					if (j.getValue().getValue().isLiteral())
						continue;

					next.add(j);
				}
			}

			list.clear();
			list.addAll(next);

			for (EntityLeaf i : next) {
				branch = entityTree.link(root, i);
				path = branch.getPredicatePath();

				/*
				 * should not prune mirrored path here, consider
				 * getTracksByReleaseId, it goes from release -> artist ->
				 * track, created-::create.
				 */

				key = path.getKey();

				if (groupMap.containsKey(key)) {
					groupMap.get(key).add(i);
				} else {
					egroup = new EntityGroup(path);
					egroup.add(i);
					groupMap.put(key, egroup);
					groupMapList.put(d, egroup);
				}
			}
		}

		distance = d;
	}

	public HtmlTable toHtmlTable() {
		TableData data = new TableData();
		TableTuple tuple = new TableTuple();

		tuple.add("Distance", "Predicate Path", "Entities");
		data.add(tuple);

		for (Entry<Integer, List<EntityGroup>> e : groupMapList.entrySet()) {

			for (EntityGroup g : e.getValue()) {
				tuple = new TableTuple();
				tuple.add(e.getKey());
				tuple.add(String.format("%s (%d)", g.getKey(), g.getNodes()
						.size()));
				tuple.add(g.toNodeString());

				data.add(tuple);
			}
		}

		return new HtmlTable("Entity Grouping "
				+ entityTree.getConcreteRoot().getValue().getValue(), data,
				false);
	}

	@Override
	public String toString() {
		StringBuilder sbuilder = new StringBuilder();

		for (Entry<Integer, List<EntityGroup>> e : groupMapList.entrySet()) {
			sbuilder.append(String.format("\nDistance %d, %d groups\n",
					e.getKey(), e.getValue().size()));

			for (EntityGroup g : e.getValue()) {
				sbuilder.append(g);
				sbuilder.append("\n");
			}
		}

		return sbuilder.toString();
	}
}
